using System;
using System.IO;
using Microsoft.Build.BuildEngine;
using Microsoft.Build.Framework;
using AntEater.ComponentModel;

using MSBuildProject = Microsoft.Build.BuildEngine.Project;

namespace AntEater.Runtime.Services.Building
{
    /// <summary>
    /// Implementation of the IBuildingService Interface for MSBuild.
    /// </summary>
    public class MSBuildBuildingService : RuntimeServiceBase, IBuildingService
    {
        private Engine _engine;

        /// <summary>
        /// Initializes a new instance of the <see cref="MSBuildBuildingService"/> class.
        /// </summary>
        public MSBuildBuildingService() {
            this._engine = new Engine();
            this._externalBuildProperties = new ExternalBuildPropertyCollection();
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        /// <param name="context">The context.</param>
        public override void Initialize(RuntimeServiceContext context) {

            if (context == null) { throw new ArgumentNullException("context"); }

            // initailize service
            base.Initialize(context);
            this.BuildEnginePath = context.Options.BuildEnginePath;

            // register a default logger
            IBuildingLogger logger = new MSBuildBuildingLogger();
            logger.Initialize(new BuildingLoggerContext(context.Output, context.Options.BuildLoggingVerbosity));
            this.RegisterLogger(logger);
        }

        /// <summary>
        /// Shutdowns this instance.
        /// </summary>
        public override void Shutdown() {
            _engine.UnregisterAllLoggers();
            base.Shutdown();
        }

        /// <summary>
        /// Gets a value indicating whether this instance is reusable.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is reusable; otherwise, <c>false</c>.
        /// </value>
        public override bool IsReusable {
            get { return false; }
        }

        /// <summary>
        /// Gets or sets the build engine path.
        /// </summary>
        /// <value>The build engine path.</value>
        public string BuildEnginePath {
            get { return _engine.BinPath; }
            set { _engine.BinPath = value; }
        }

        private ExternalBuildPropertyCollection _externalBuildProperties;

        /// <summary>
        /// Gets the external build properties.
        /// </summary>
        /// <value>The external build properties.</value>
        public ExternalBuildPropertyCollection ExternalBuildProperties {
            get { return _externalBuildProperties; }
        }

        /// <summary>
        /// Registers the logger.
        /// </summary>
        /// <param name="logger">The logger.</param>
        public void RegisterLogger(IBuildingLogger logger) {

            if (logger == null) { throw new ArgumentNullException("logger"); }

            // Register logger by the engine
            ILogger buildLogger = logger as ILogger;
            if (buildLogger == null) {
                throw new ArgumentException("logger is not type of Microsoft.Build.Framework.ILogger.", "logger");
            } else {
                _engine.RegisterLogger(buildLogger);
            }
        }

        /// <summary>
        /// Builds the specified build file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        public bool Build(Stream stream, string filename) {
            return this.Build(stream, filename, new string[] { });
        }

        /// <summary>
        /// Builds the specified build file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="filename">The filename.</param>
        /// <param name="target">The target.</param>
        /// <returns></returns>
        public bool Build(Stream stream, string filename, string target) {
            return this.Build(stream, filename, new string[] { target });
        }

        /// <summary>
        /// Builds the specified build file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="filename">The filename.</param>
        /// <param name="targets">The targets.</param>
        /// <returns></returns>
        public bool Build(Stream stream, string filename, string[] targets) {

            if (stream == null) { throw new ArgumentNullException("stream"); }
            if (filename == null) { throw new ArgumentNullException("filename"); }
            if (targets == null) { throw new ArgumentNullException("targets"); }

            bool result = false;
            
            // Create MS Build Project
            MSBuildProject project = new MSBuildProject(_engine);
            project.FullFileName = filename;

            // Load the project
            using (StreamReader reader = new StreamReader(stream)) {
                project.Load(reader);
            }

            // Pass external build properties to the project
            foreach (ExternalBuildProperty property in _externalBuildProperties) {
                project.SetProperty(property.Name, property.Value);
            }

            // Build project
            if (targets.Length > 0) {
                result = _engine.BuildProject(project, targets);
            } else {
                result = _engine.BuildProject(project);
            }

            return result;
        }
    }
}
